﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Text;
using System.Web;
using System.Linq;

namespace FastData.DataProvider
{
    public abstract class DataProviderFactory
    {
        public static DataProviderFactory GetFactory(string providerName)
        {
            switch (providerName)
            {
                case "System.Data.SqlClient":
                    return new SqlProviderFactory();
                case "System.Data.SqlServerCe.4.0":
                    return new SqlCeProviderFactory();
                case "Oracle.Data.Client":
                    return new OracleProviderFactory();
                default:
                    throw new NotImplementedException(providerName);
            }
        }

        public readonly List<KeyValuePair<string, string>> Correction = new List<KeyValuePair<string, string>>();
        public abstract SelectQuery GetViewTableUsage(DataAccess data, string viewName);
        public abstract string GetDbObjectName(string name);
        public abstract string GetDbParameterName(string name);
        public abstract string ProviderName { get; }
        public abstract bool SupportBatchCommand { get; }
        public abstract bool SupportInsertedId { get; }

        internal DbProviderFactory ProviderFactory;
        internal abstract string LastIdentityText { get; }
        internal abstract string CreateSelectText(QueryBase query, ExecuteType type);

        public DataProviderFactory()
        {
            ProviderFactory = DbProviderFactories.GetFactory(ProviderName);
        }

        internal DbConnection CreateConnection()
        {
            return ProviderFactory.CreateConnection();
        }

        internal DbCommand CreateCommand()
        {
            return ProviderFactory.CreateCommand();
        }

        internal DbParameter CreateParameter(string name, object value)
        {
            return CreateParameter(name, value, false);
        }

        internal DbParameter CreateParameter(string name, object value, bool antiXSS)
        {
            DbParameter dbParameter = ProviderFactory.CreateParameter();
            dbParameter.ParameterName = name;
            dbParameter.Value = GetSafeValue(value, antiXSS);

            return dbParameter;
        }

        protected virtual object GetSafeValue(object value, bool antiXSS)
        {
            if (value == null)
            {
                return DBNull.Value;
            }

            string stringValue = value as string;
            if (stringValue == null)
            {
                return value;
            }

            if (stringValue == string.Empty)
            {
                return DBNull.Value;
            }

            stringValue = CorrectValue(stringValue);

            return antiXSS ? HttpUtility.HtmlEncode(stringValue) : stringValue;
        }

        private string CorrectValue(string value)
        {
            foreach (var item in Correction)
            {
                value = value.Replace(item.Key, item.Value);
            }
            return value;
        }

        internal virtual string CreateProcedureText(Procedure query, ExecuteType type)
        {
            return GetDbObjectName(query.Name);
        }

        internal virtual string CreateFunctionText(Function query, ExecuteType type)
        {
            return GetDbObjectName(query.Name) + "(" + string.Join(",", query.Parameters.Select(p => GetDbParameterName(p.ParameterName))) + ")";
        }

        internal virtual string CreateInsertText(QueryBase query, ExecuteType type)
        {
            string parameterNames = string.Empty;
            string fieldNames = string.Empty;
            foreach (DbParameter field in query.Fields)
            {
                parameterNames += "," + GetDbParameterName(field.ParameterName);
                fieldNames += "," + GetDbObjectName(field.ParameterName);
            }
            if (parameterNames == string.Empty)
            {
                throw new Exception("هیچ پارامتری برای ذخیره وجود ندارد");
            }

            return "Insert Into " + GetDbObjectName(query.TableName) + " (" + fieldNames.Substring(1) + ") Values(" + parameterNames.Substring(1) + ")";
        }

        internal virtual string CreateBatchText(QueryBase query, ExecuteType type, Query batchQuery)
        {
            return query.GetCommandText(type) + ";";
        }

        internal virtual Query CreateBatchQuery(List<QueryBase> queries, ExecuteType type)
        {
            Query query = queries[0].Data.Query("");

            for (int i = 0; i < queries.Count; i++)
            {
                string commandText = CreateBatchText(queries[i], type, query);

                foreach (DbParameter item in queries[i].Parameters)
                {
                    string newParameterName = "query" + i + item.ParameterName;
                    commandText = commandText.Replace(GetDbParameterName(item.ParameterName), GetDbParameterName(newParameterName));
                    item.ParameterName = newParameterName;
                    query.Param(item);
                }

                query.CommandText += commandText;
            }

            return query;
        }

        internal virtual string CreateUpdateText(QueryBase query, ExecuteType type)
        {
            string updates = string.Empty;
            foreach (DbParameter field in query.Fields)
            {
                updates += "," + GetDbObjectName(field.ParameterName) + "=" + GetDbParameterName(field.ParameterName);
            }
            if (updates == string.Empty)
            {
                throw new Exception("هیچ پارامتری برای ذخیره وجود ندارد");
            }

            return "Update " + GetDbObjectName(query.TableName) + " Set " + updates.Substring(1) + " " + query.WhereText;
        }

        internal virtual string CreateDeleteText(QueryBase query, ExecuteType type)
        {
            return "Delete From " + GetDbObjectName(query.TableName) + " " + query.WhereText;
        }

    }

}
